package com.cfushn.ddd.mp.crud;

import com.alibaba.fastjson.JSON;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import org.apache.ibatis.annotations.Delete;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Options;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.annotations.Update;
import org.apache.ibatis.mapping.StatementType;

/**
 * 通用crud接口
 *
 * @author CFuShn 2022/3/31 18:47
 */
@SuppressWarnings("unused")
public interface Crud {

    /**
     * 插入
     *
     * @param sql String
     * @return int
     * @author CFuShn at 2022/3/31 19:10
     */
    @Insert("${sql}")
    int insert(@Param("sql") String sql);

    /**
     * 删除
     *
     * @param sql sql
     * @return int
     * @author CFuShn at 2022/3/31 19:10
     */
    @Delete("${sql}")
    int delete(@Param("sql") String sql);

    /**
     * 修改
     *
     * @param sql sql
     * @return int
     * @author CFuShn at 2022/3/31 19:10
     */
    @Update("${sql}")
    int update(@Param("sql") String sql);

    /**
     * 查询,支持接收任意结果集
     *
     * @param sql sql
     * @return List<Map < String column, Object value>>
     */
    @Select("${sql}")
    List<Map<String, Object>> select(@Param("sql") String sql);

    /**
     * 调用存储过程
     *
     * @param sql sql
     */
    @Select("${sql}")
    @Options(statementType = StatementType.CALLABLE)
    void callProcedure(@Param("sql") String sql);

    /**
     * 查询单列值list或实体类List,返回List
     * (*注:查询实体类结果放入容器时,同名列类型必须兼容)
     *
     * @param sql   String
     * @param clazz 单列值对应的类型,或实体类类型
     * @return List
     * @author CFuShn at 2022/2/18 18:38
     */
    @SuppressWarnings("all")
    default <T> List<T> select(String sql, Class<T> clazz) {
        List<Map<String, Object>> mapList = select(sql);
        if (mapList != null && !mapList.isEmpty() && mapList.get(0) != null) {
            Object sniffObj = mapList.get(0).values().iterator().next();
            if (clazz.equals(String.class) || (clazz.isAssignableFrom(Number.class)
                && sniffObj instanceof Number)) {
                // 查询的结果明确是字符串,或者数字型
                return mapList.stream().map(recordMap -> (T) recordMap.values().iterator().next())
                    .collect(Collectors.toList());
            } else {
                // 否则认为是个对象
                return mapList.stream()
                    .map(recordMap -> JSON.parseObject(JSON.toJSONString(recordMap), clazz))
                    .collect(Collectors.toList());
            }
        }
        return Collections.emptyList();
    }

    /**
     * 查询单值或单个实体类, 如果结果集不是单值或单个实体类,则报错
     *
     * @param sql   sql
     * @param clazz 单值或单个实体类的类型
     * @return 单值或单个实体类
     * @author CFuShn at 2022/1/19 16:11
     */
    default <T> T selectOne(String sql, Class<T> clazz) {
        List<T> list = select(sql, clazz);
        if (list == null || list.isEmpty()) {
            return null;
        }
        if (list.size() > 1) {
            throw new RuntimeException("查询结果不唯一,请检查sql正确性!");
        }
        return list.get(0);
    }

    /**
     * 刷新物化视图
     *
     * @param mv             物化视图
     * @param isConcurrently 是否增量刷新 (是: 增量; 否: 全量)
     */
    default void refreshMv(String mv, boolean isConcurrently) {
        callProcedure(String.format("refresh materialized view %s %s with data",
            isConcurrently ? "concurrently" : "", mv));
    }
}
